#include "pdtdw.hpp"
#ifndef __SYNTHESIS__ // 仅在 C 仿真时包含 iostream 和 iomanip
#include <iostream>
#include <iomanip>  // 用于std::hex, std::dec
#endif

// -----------------------------------------------------------
// PDTD (Pixel Depth Test & Dispatch) 模块实现
// 职责：从HR接收像素，过滤无效像素，将有效的像素推入DDR写队列
// -----------------------------------------------------------
void pixel_depth_test_and_dispatch(
    hls::stream<pixel_fragment_packet_fixed>& input_pixel_fragment_stream,
    hls::stream<ddr_write_packet_fixed>& output_ddr_write_stream,
    const ap_uint<11> max_x_res,
    const ap_uint<11> max_y_res,
    hls::stream<ap_uint<32>>& input_fragments_count_stream,
    hls::stream<ap_uint<32>>& output_valid_fragments_count_stream // 输出有效像素计数
) {
    #pragma HLS INTERFACE axis port=input_pixel_fragment_stream
    #pragma HLS INTERFACE axis port=output_ddr_write_stream 
    #pragma HLS INTERFACE axis port=input_fragments_count_stream
    #pragma HLS INTERFACE axis port=output_valid_fragments_count_stream

    #pragma HLS INTERFACE s_axilite port=max_x_res bundle=ctrl_s_axilite_pdtd
    #pragma HLS INTERFACE s_axilite port=max_y_res bundle=ctrl_s_axilite_pdtd
    #pragma HLS INTERFACE s_axilite port=return bundle=ctrl_s_axilite_pdtd

    #pragma HLS DATAFLOW

    ap_uint<32> num_fragments_to_process; 
    input_fragments_count_stream.read(num_fragments_to_process); 
#ifndef __SYNTHESIS__ // Debug: Print received fragment count
    std::cout << "[PDTD DEBUG] Received total fragments from HR: " << num_fragments_to_process << std::endl;
#endif

    ap_uint<32> valid_fragment_counter = 0;

    for (ap_uint<32> i = 0; i < num_fragments_to_process; ++i) { 
        #pragma HLS PIPELINE II=1
        
        pixel_fragment_packet_fixed fragment;
        input_pixel_fragment_stream.read(fragment); 

        if (fragment.is_valid_pixel) {
            ddr_write_packet_fixed write_op;
            write_op.screen_x = fragment.screen_x;
            write_op.screen_y = fragment.screen_y;
            write_op.new_z     = fragment.interpolated_z;
            write_op.pure_color_R = fragment.pure_color_R;
            write_op.pure_color_G = fragment.pure_color_G;
            write_op.pure_color_B = fragment.pure_color_B;

            output_ddr_write_stream.write(write_op);
            valid_fragment_counter++;

#ifndef __SYNTHESIS__ // Debug: Print valid fragments output by PDTD (limited frequency)
            if (valid_fragment_counter < 10 || valid_fragment_counter % 10000 == 0) {
                std::cout << "[PDTD DEBUG]   Valid Fragment output: (" << write_op.screen_x << "," 
                          << write_op.screen_y << "), Z: " << write_op.new_z 
                          << ", Color: (" << (int)write_op.pure_color_R << "," 
                          << (int)write_op.pure_color_G << "," << (int)write_op.pure_color_B << ")" << std::endl;
            }
#endif
        }
    }
    output_valid_fragments_count_stream.write(valid_fragment_counter);
#ifndef __SYNTHESIS__ // Debug: Print total valid fragments
    std::cout << "[PDTD DEBUG] Total valid fragments output by PDTD: " << valid_fragment_counter << std::endl;
#endif
}


// ===========================================================
// Sub-process 1: Calculate addresses and issue Z-buffer RMW request
// ===========================================================
void dwe_calc_addr_and_z_rmw_req(
    hls::stream<ddr_write_packet_fixed>& input_ddr_write_stream,
    hls::stream<ap_uint<32>>& input_valid_fragments_count_stream,
    hls::stream<z_rmw_request_packet>& output_z_rmw_req_stream,
    hls::stream<ap_uint<32>>& output_z_rmw_req_count_stream, // 输出请求计数给下一阶段
    const ap_uint<11> max_x_res
) {
    const ap_uint<32> Z_PER_WORD = 4; // 64 bits / 16 bits = 4 depth values per word
    
    ap_uint<32> num_valid_fragments_to_process;
    input_valid_fragments_count_stream.read(num_valid_fragments_to_process);
#ifndef __SYNTHESIS__ // Debug: Print received valid fragments count
    std::cout << "[DWE_CALC_ADDR_DEBUG] Received valid fragments count from PDTD: " << num_valid_fragments_to_process << std::endl;
#endif

    // 将请求计数传递给下一个 DATAFLOW 阶段
    output_z_rmw_req_count_stream.write(num_valid_fragments_to_process);
    
#ifndef __SYNTHESIS__ // Debug: Counter for limited print frequency
    ap_uint<32> processed_req_count_calc_addr = 0;
#endif

    READ_INPUT_LOOP:
    for (ap_uint<32> i = 0; i < num_valid_fragments_to_process; ++i) {
        #pragma HLS PIPELINE II=1
        ddr_write_packet_fixed write_op;
        input_ddr_write_stream.read(write_op);

        ap_uint<32> pixel_linear_idx = write_op.screen_y * max_x_res + write_op.screen_x;

        ap_uint<32> z_word_idx = pixel_linear_idx / Z_PER_WORD;
        ap_uint<3> z_sub_word_offset = pixel_linear_idx % Z_PER_WORD;

        z_rmw_request_packet req;
        req.z_word_idx = z_word_idx;
        req.z_sub_word_offset = z_sub_word_offset;
        req.write_op = write_op;
        output_z_rmw_req_stream.write(req);

#ifndef __SYNTHESIS__ // Debug: Print address calculation results (limited frequency)
        processed_req_count_calc_addr++;
        if (processed_req_count_calc_addr < 10 || processed_req_count_calc_addr % 10000 == 0) {
            std::cout << "[DWE_CALC_ADDR_DEBUG]   Px(" << write_op.screen_x << "," << write_op.screen_y 
                      << "), LinearIdx: " << pixel_linear_idx << ", NewZ: " << write_op.new_z
                      << ", Z_WordIdx: " << z_word_idx << ", Z_SubOffset: " << z_sub_word_offset << std::endl;
        }
#endif
    }
}


// ===========================================================
// Sub-process 2: Z-Buffer Write-Combining Cache (Write-Back Policy)
// ===========================================================

// Define cache parameters
const int CACHE_NUM_LINES = 64; // Example size
const int CACHE_ADDR_BITS = 6; // log2(CACHE_NUM_LINES)

void dwe_z_buffer_write_cache(
    hls::stream<z_rmw_request_packet>& input_z_rmw_req_stream,
    hls::stream<ap_uint<32>>& input_z_rmw_req_count_stream,
    hls::stream<fb_write_request_packet>& output_fb_write_req_stream,
    hls::stream<ap_uint<32>>& output_fb_write_req_count_stream,
    hls::stream<ddr_op_count_packet>& output_actual_z_write_stream, // Each packet is a successful Z-write to DDR
    hls::stream<ap_uint<32>>& output_actual_z_write_count_stream,
    zbuffer_ddr_word_t* output_zbuffer_ddr_read,   // M_AXI port for Z-buffer read
    zbuffer_ddr_word_t* output_zbuffer_ddr_write, // M_AXI port for Z-buffer write
    const ap_uint<11> max_x_res
) {
    #pragma HLS DATAFLOW

    const ap_uint<32> Z_PER_WORD = 4; // 64 bits / 16 bits = 4 depth values per word
    const ap_uint<64> Z_MASK_16BIT = 0xFFFF;
    const ap_uint<32> COLOR_PER_WORD = 2; // for framebuffer calculation

    static zbuffer_ddr_word_t cache_data[CACHE_NUM_LINES];
    static ap_uint<32> cache_tag[CACHE_NUM_LINES]; // Stores z_word_idx as tag
    static bool cache_valid[CACHE_NUM_LINES];
    static bool cache_dirty[CACHE_NUM_LINES];
    
    #pragma HLS ARRAY_PARTITION variable=cache_data complete dim=1
    #pragma HLS ARRAY_PARTITION variable=cache_tag complete dim=1
    #pragma HLS ARRAY_PARTITION variable=cache_valid complete dim=1
    #pragma HLS ARRAY_PARTITION variable=cache_dirty complete dim=1

    // Initialize cache once at the start (C-simulation only, HLS handles static init)
#ifndef __SYNTHESIS__
    static bool initialized = false;
    if (!initialized) {
        for (int i = 0; i < CACHE_NUM_LINES; ++i) {
            cache_valid[i] = false;
            cache_dirty[i] = false;
            cache_data[i] = 0;
            cache_tag[i] = 0;
        }
        initialized = true;
    }
#endif

    ap_uint<32> num_rmw_requests;
    input_z_rmw_req_count_stream.read(num_rmw_requests);
#ifndef __SYNTHESIS__
    std::cout << "[DWE_Z_CACHE_DEBUG] Z-Buffer write cache started with " << num_rmw_requests << " requests." << std::endl;
#endif

    // =========================================================================
    // Stage 1: Process incoming requests and perform Z-test/update on cache
    // =========================================================================
    ap_uint<32> fb_write_count = 0;
    ap_uint<32> z_actual_write_count = 0; // Tracks actual Z-writes to DDR (via cache evict/flush)

    PROCESS_REQUESTS_LOOP:
    for (ap_uint<32> i = 0; i < num_rmw_requests; ++i) {
        #pragma HLS LOOP_TRIPCOUNT min=100000 max=500000

        z_rmw_request_packet req;
        input_z_rmw_req_stream.read(req);

        ap_uint<32> target_z_word_idx = req.z_word_idx;
        ap_uint<CACHE_ADDR_BITS> cache_line_idx = target_z_word_idx(CACHE_ADDR_BITS - 1, 0); // Modulo operation for direct-mapped

        zbuffer_ddr_word_t current_z_word;
        bool cache_hit = (cache_valid[cache_line_idx] && cache_tag[cache_line_idx] == target_z_word_idx);

        if (cache_hit) {
            current_z_word = cache_data[cache_line_idx];
#ifndef __SYNTHESIS__
            if (i < 10 || i % 10000 == 0) {
                std::cout << "[DWE_Z_CACHE_DEBUG]   Cache HIT for Z_WordIdx: " << target_z_word_idx << " (line " << cache_line_idx << ")" << std::endl;
            }
#endif
        } else { // Cache Miss - Implement Write-Back policy for eviction
            if (cache_valid[cache_line_idx] && cache_dirty[cache_line_idx]) {
                output_zbuffer_ddr_write[cache_tag[cache_line_idx]] = cache_data[cache_line_idx]; // Write-back
                ddr_op_count_packet count_pkt; // Increment actual write count
                count_pkt.is_valid_write = true;
                output_actual_z_write_stream.write(count_pkt);
                z_actual_write_count++;
#ifndef __SYNTHESIS__
                if (i < 10 || i % 10000 == 0) {
                    std::cout << "[DWE_Z_CACHE_DEBUG]   Cache MISS & EVICT (dirty) for line " << cache_line_idx << ", flushing old data 0x" 
                              << std::hex << cache_data[cache_line_idx] << std::dec << " to addr " << cache_tag[cache_line_idx] << std::endl;
                }
#endif
            }
            // Fetch new data from DDR (using read port)
            current_z_word = output_zbuffer_ddr_read[target_z_word_idx];
            
            // Update cache line with new data
            cache_data[cache_line_idx] = current_z_word;
            cache_tag[cache_line_idx] = target_z_word_idx;
            cache_valid[cache_line_idx] = true;
            cache_dirty[cache_line_idx] = false; // Just read, so not dirty yet

#ifndef __SYNTHESIS__
            if (i < 10 || i % 10000 == 0) {
                std::cout << "[DWE_Z_CACHE_DEBUG]   Cache MISS for Z_WordIdx: " << target_z_word_idx << " (line " << cache_line_idx << "), fetched 0x" 
                          << std::hex << current_z_word << std::dec << " from DDR." << std::endl;
            }
#endif
        }

        fpal::fp_z_buffer_t old_z = (fpal::fp_z_buffer_t)((current_z_word >> (req.z_sub_word_offset * 16)) & Z_MASK_16BIT);

#ifndef __SYNTHESIS__
        if (i < 10 || i % 10000 == 0) {
            std::cout << "[DWE_Z_CACHE_DEBUG]     RMW Request Px(" << req.write_op.screen_x << "," << req.write_op.screen_y 
                      << "), NewZ: " << req.write_op.new_z << ", OldZ: " << old_z;
            if (req.write_op.new_z < old_z) {
                std::cout << " -> Z-Test PASSED." << std::endl;
            } else {
                std::cout << " -> Z-Test FAILED." << std::endl;
            }
        }
#endif

        if (req.write_op.new_z < old_z) {
            // Modify the 64-bit word with the new Z-value
            zbuffer_ddr_word_t new_z_word_val = current_z_word;
            new_z_word_val &= ~(Z_MASK_16BIT << (req.z_sub_word_offset * 16)); // Clear old Z-value
            new_z_word_val |= ((zbuffer_ddr_word_t)req.write_op.new_z << (req.z_sub_word_offset * 16)); // Set new Z-value
            
            // Update cache line with modified value
            cache_data[cache_line_idx] = new_z_word_val;
            cache_dirty[cache_line_idx] = true; // Mark as dirty

            // Calculate color_word_idx for FB write and pass to next stage
            ap_uint<32> pixel_linear_idx = req.write_op.screen_y * max_x_res + req.write_op.screen_x;
            ap_uint<32> color_word_idx = pixel_linear_idx / COLOR_PER_WORD;
            ap_uint<1> color_sub_word_offset = pixel_linear_idx % COLOR_PER_WORD;

            fb_write_request_packet fb_req;
            fb_req.color_word_idx = color_word_idx;
            fb_req.color_sub_word_offset = color_sub_word_offset;
            fb_req.pure_color_R = req.write_op.pure_color_R;
            fb_req.pure_color_G = req.write_op.pure_color_G;
            fb_req.pure_color_B = req.write_op.pure_color_B;
            output_fb_write_req_stream.write(fb_req);
            fb_write_count++;

#ifndef __SYNTHESIS__
            if (i < 10 || i % 10000 == 0) {
                std::cout << "[DWE_Z_CACHE_DEBUG]     Updated cache line " << cache_line_idx << " for Z_WordIdx " << target_z_word_idx << " to 0x" 
                          << std::hex << new_z_word_val << std::dec << ". Marked dirty." << std::endl;
            }
#endif
        }
    } // End of PROCESS_REQUESTS_LOOP

    // =========================================================================
    // Stage 2: Flush remaining dirty cache lines to DDR (using write port)
    // =========================================================================
    FLUSH_CACHE_LOOP:
    for (int k = 0; k < CACHE_NUM_LINES; ++k) {
        #pragma HLS PIPELINE II=1
        if (cache_valid[k] && cache_dirty[k]) {
            output_zbuffer_ddr_write[cache_tag[k]] = cache_data[k]; // Write-back
            ddr_op_count_packet count_pkt; // Increment actual write count
            count_pkt.is_valid_write = true;
            output_actual_z_write_stream.write(count_pkt);
            z_actual_write_count++;
#ifndef __SYNTHESIS__
            std::cout << "[DWE_Z_CACHE_DEBUG]   FLUSH: Writing dirty cache line " << k << " (addr " << cache_tag[k] << ") 0x" 
                      << std::hex << cache_data[k] << std::dec << " to DDR." << std::endl;
#endif
        }
    }
    
    // Pass counts to downstream modules for deterministic termination
    output_fb_write_req_count_stream.write(fb_write_count);
    output_actual_z_write_count_stream.write(z_actual_write_count); // This count is for actual DDR writes

#ifndef __SYNTHESIS__
    std::cout << "[DWE_Z_CACHE_DEBUG] Finished Z-buffer write cache processing. Total FB writes: " << fb_write_count 
              << ", Total Z-writes to DDR (via cache): " << z_actual_write_count << std::endl;
#endif
}


// ===========================================================
// Sub-process 3: Perform Framebuffer Read-Modify-Write
// ===========================================================
void dwe_fb_rmw(
    hls::stream<fb_write_request_packet>& input_fb_write_req_stream,
    hls::stream<ap_uint<32>>& input_fb_write_req_count_stream,
    color_ddr_word_t* output_framebuffer_ddr // M_AXI port
) {
    const ap_uint<64> PIXEL_SLOT_MASK = 0xFFFFFFFF; // For 32-bit color slots within 64-bit word

    ap_uint<32> num_fb_writes_to_process;
    input_fb_write_req_count_stream.read(num_fb_writes_to_process);
#ifndef __SYNTHESIS__
    std::cout << "[DWE_FB_RMW_DEBUG] Starting FB RMW for " << num_fb_writes_to_process << " requests." << std::endl;
#endif

#ifndef __SYNTHESIS__ // Debug: Counter for limited print frequency
    ap_uint<32> processed_req_count_fb_rmw = 0;
#endif

    // Use explicit loop count for robust termination
    READ_FB_BUFFER_LOOP:
    for (ap_uint<32> i = 0; i < num_fb_writes_to_process; ++i) {
        #pragma HLS PIPELINE II=1
        fb_write_request_packet req;
        input_fb_write_req_stream.read(req);

        // --- AXI READ for Framebuffer ---
        color_ddr_word_t current_fb_word = output_framebuffer_ddr[req.color_word_idx];

        // Pack new color (RGB888) into a 32-bit slot (e.g., 0x00RRGGBB)
        ap_uint<32> new_packed_color = 0;
        new_packed_color |= ((ap_uint<32>)req.pure_color_R << 16);
        new_packed_color |= ((ap_uint<32>)req.pure_color_G << 8);
        new_packed_color |= ((ap_uint<32>)req.pure_color_B << 0);

        // Modify the 64-bit word
        color_ddr_word_t modified_color_word = current_fb_word;
        if (req.color_sub_word_offset == 0) { // First pixel slot (lower 32 bits)
            modified_color_word &= ~(PIXEL_SLOT_MASK << 0);
            modified_color_word |= ((color_ddr_word_t)new_packed_color << 0);
        } else { // Second pixel slot (upper 32 bits)
            modified_color_word &= ~(PIXEL_SLOT_MASK << 32);
            modified_color_word |= ((color_ddr_word_t)new_packed_color << 32);
        }

        // --- AXI WRITE for Framebuffer ---
        output_framebuffer_ddr[req.color_word_idx] = modified_color_word;

#ifndef __SYNTHESIS__ // Debug: Print FB write details (limited frequency)
        processed_req_count_fb_rmw++;
        if (processed_req_count_fb_rmw < 10 || processed_req_count_fb_rmw % 10000 == 0) {
            std::cout << "[DWE_FB_RMW_DEBUG]   Wrote FB word at idx " << req.color_word_idx << " (offset " 
                      << req.color_sub_word_offset << "), New Color: 0x" << std::hex << new_packed_color 
                      << ", Full Word: 0x" << modified_color_word << std::dec << std::endl;
        }
#endif
    }
#ifndef __SYNTHESIS__ // Debug: Print total FB writes
    std::cout << "[DWE_FB_RMW_DEBUG] Total Framebuffer writes: " << processed_req_count_fb_rmw << std::endl;
#endif
}

// ===========================================================
// Sub-process 4: Aggregate write operations count
// ===========================================================
void dwe_count_aggregator(
    hls::stream<ddr_op_count_packet>& input_actual_z_write_stream, // Each 'true' signifies a successful Z-write
    hls::stream<ap_uint<32>>& input_actual_z_write_count_stream, // Total successful Z-writes count
    hls::stream<ap_uint<32>>& output_final_count_stream // Output final count
) {
    ap_uint<32> num_actual_z_writes_to_process;
    input_actual_z_write_count_stream.read(num_actual_z_writes_to_process);
#ifndef __SYNTHESIS__
    std::cout << "[DWE_COUNT_AGG_DEBUG] Starting count aggregation for " << num_actual_z_writes_to_process << " Z-writes." << std::endl;
#endif

    ap_uint<32> write_ops_counter = 0;

    // Use explicit loop count for robust termination
    COUNT_LOOP:
    for (ap_uint<32> i = 0; i < num_actual_z_writes_to_process; ++i) {
        #pragma HLS PIPELINE II=1
        ddr_op_count_packet pkt;
        input_actual_z_write_stream.read(pkt); // Simply read the 'true' signal
        if (pkt.is_valid_write) { // Should always be true as only successful writes are streamed
            write_ops_counter++;
        }
    }
    output_final_count_stream.write(write_ops_counter);
#ifndef __SYNTHESIS__ // Debug: Print final aggregated count
    std::cout << "[DWE_COUNT_AGG_DEBUG] Final aggregated write operations count: " << write_ops_counter << std::endl;
#endif
}


// ===========================================================
// DDR_Write_Engine 模块实现 (顶层函数)
// ===========================================================
void ddr_write_engine(
    hls::stream<ddr_write_packet_fixed>& input_ddr_write_stream,
    hls::stream<ap_uint<32>>& input_valid_fragments_count_stream,
    const ap_uint<11> max_x_res,
    const ap_uint<11> max_y_res,
    zbuffer_ddr_word_t* output_zbuffer_ddr_read,   // Read-only pointer
    zbuffer_ddr_word_t* output_zbuffer_ddr_write, // Write-only pointer
    color_ddr_word_t* output_framebuffer_ddr,         // Framebuffer pointer (remains R/W for FB RMW)
    ap_uint<32>& output_actual_write_ops
) {
    #pragma HLS INTERFACE s_axilite port=max_x_res bundle=ctrl_s_axilite_dwe
    #pragma HLS INTERFACE s_axilite port=max_y_res bundle=ctrl_s_axilite_dwe
    #pragma HLS INTERFACE s_axilite port=output_actual_write_ops bundle=ctrl_s_axilite_dwe
    #pragma HLS INTERFACE s_axilite port=return bundle=ctrl_s_axilite_dwe

    // 分离 Z-buffer 的读写端口
    #pragma HLS INTERFACE m_axi port=output_zbuffer_ddr_read  offset=slave bundle=m_axi_gmem_zb_read  max_read_burst_length=256 latency=60 num_read_outstanding=32 depth=524288 // 修正 depth
    #pragma HLS INTERFACE m_axi port=output_zbuffer_ddr_write offset=slave bundle=m_axi_gmem_zb_write max_write_burst_length=256 latency=60 num_write_outstanding=32 depth=524288 // 修正 depth
    
    #pragma HLS INTERFACE m_axi port=output_framebuffer_ddr offset=slave bundle=m_axi_gmem_fb_write max_read_burst_length=256 max_write_burst_length=256 latency=60 num_read_outstanding=32 num_write_outstanding=32 depth=524288 // 修正 depth
    
    #pragma HLS INTERFACE axis port=input_ddr_write_stream
    #pragma HLS INTERFACE axis port=input_valid_fragments_count_stream

    #pragma HLS DATAFLOW

    hls::stream<z_rmw_request_packet> z_rmw_req_stream("z_rmw_req_stream");
    #pragma HLS STREAM variable=z_rmw_req_stream depth=16 // Increased depth for potentially more parallel requests

    hls::stream<fb_write_request_packet> fb_write_req_stream("fb_write_req_stream");
    #pragma HLS STREAM variable=fb_write_req_stream depth=16

    hls::stream<ddr_op_count_packet> actual_z_write_stream("actual_z_write_stream");
    #pragma HLS STREAM variable=actual_z_write_stream depth=16 // Stream of successful Z-writes

    hls::stream<ap_uint<32>> dwe_final_count_stream("dwe_final_count_stream");
    #pragma HLS STREAM variable=dwe_final_count_stream depth=2

    hls::stream<ap_uint<32>> z_rmw_req_count_stream("z_rmw_req_count_stream");
    #pragma HLS STREAM variable=z_rmw_req_count_stream depth=2
    
    hls::stream<ap_uint<32>> fb_write_req_count_stream("fb_write_req_count_stream");
    #pragma HLS STREAM variable=fb_write_req_count_stream depth=2

    hls::stream<ap_uint<32>> actual_z_write_count_stream("actual_z_write_count_stream");
    #pragma HLS STREAM variable=actual_z_write_count_stream depth=2

    // Sub-process 1: Calculate addresses and issue Z-buffer RMW requests
    dwe_calc_addr_and_z_rmw_req(
        input_ddr_write_stream,
        input_valid_fragments_count_stream,
        z_rmw_req_stream,
        z_rmw_req_count_stream, // Pass request count to the next stage
        max_x_res
    );

    // Sub-process 2: Z-Buffer Write-Combining Cache (This is the critical part for consistency)
    // All Z-buffer RMW operations now go through this cache.
    dwe_z_buffer_write_cache(
        z_rmw_req_stream,
        z_rmw_req_count_stream, // Receive request count
        fb_write_req_stream,
        fb_write_req_count_stream, // Output count for FB writes
        actual_z_write_stream,
        actual_z_write_count_stream, // Output count for actual Z-writes (for aggregator)
        output_zbuffer_ddr_read,    // Pass read port
        output_zbuffer_ddr_write,   // Pass write port
        max_x_res
    );

    // Sub-process 3: Framebuffer Read-Modify-Write
    dwe_fb_rmw(
        fb_write_req_stream,
        fb_write_req_count_stream, // Receive FB write count
        output_framebuffer_ddr
    );

    // Sub-process 4: Aggregate write operations count
    dwe_count_aggregator(
        actual_z_write_stream,
        actual_z_write_count_stream, // Receive actual Z-write count
        dwe_final_count_stream
    );

    dwe_final_count_stream.read(output_actual_write_ops);
}
